/**********************************************************************************
***
***    Screencasting Teaching System C++ Library
***    Copyright (C) 2011  Jiri Novak <jiri.novak@petriny.net>
***                        Wadi Jalil Maluf <wadijm@gmail.com>
***
***    This file is part of the Screencasting Teaching System C++ Library.
***
***    This library is free software; you can redistribute it and/or modify
***    it under the terms of the GNU General Public License as published by
***   the Free Software Foundation; either version 3 of the License, or
***    (at your option) any later version.
***
***    This library is distributed in the hope that it will be useful,
***    but WITHOUT ANY WARRANTY; without even the implied warranty of
***    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
***   GNU General Public License for more details.
***
***    You should have received a copy of the GNU General Public License
***    along with this library; if not, write to the Free Software
***    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
***
*********************************************************************************/

#include <iostream>
#include <cassert>
#include <algorithm>
#include "main_window.h"
#include "login_dialog.h"
#include "screencasting_window.h"
#include "../serverservice.h"
#include "../servercallbackhandlerguiimpl.h"
#include "../../common/model/user.h"
#include "../../common/model/lesson.h"
#include "../../common/model/course.h"
#include "../media/capture_workspace_x11_alsa_impl.h"

/**
 *
 */
MainWindow::MainWindow(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& builder)
: Gtk::Window(cobject),
  logger_(log4cxx::Logger::getLogger("client.gui.main_window")),
  builder_(builder),
  screencastingActive_(false),
  captureWorkspace_(new CaptureWorkspaceX11AlsaImpl())
{
	selectedCourse_ = 0;
	selectedLesson_ = 0;
	capturedLesson_ = 0;

	initializeWidgets();
	initializeRelatedDialogs();
	registerRelatedDialogs2ServerServiceHandler();
	makeConnections();
	onDisconnectRequested();
}

/**
 *
 */
MainWindow::~MainWindow()
{
	onDisconnectRequested();
	destroyRelatedDialogs();
	if (captureWorkspace_)
		delete captureWorkspace_;
}

/**
 *
 */
void MainWindow::initializeRelatedDialogs()
{
	// login dialog
	builder_->get_widget_derived("login_dialog", loginDialog_);
	assert(loginDialog_ != 0 && "loginDialog_ pointer is null!");
	loginDialog_->set_transient_for(*this);

	// screencasting window
	builder_->get_widget_derived("screencasting_window", screencastingWindow_);
	assert(screencastingWindow_ != 0 && "screencastingWindow_ pointer is null!");
	screencastingWindow_->set_transient_for(*this);
}

/**
 *
 */
void MainWindow::destroyRelatedDialogs()
{
	if (loginDialog_)
		delete loginDialog_;
	if (screencastingWindow_)
		delete screencastingWindow_;
}

/**
 *
 */
void MainWindow::registerRelatedDialogs2ServerServiceHandler()
{
	ServerCallbackHandlerGuiImpl::getInstance().setMainWindow(this);
	ServerCallbackHandlerGuiImpl::getInstance().setLoginDialog(loginDialog_);
	ServerCallbackHandlerGuiImpl::getInstance().setScreencastingWindow(screencastingWindow_);
}

/**
 *
 */
void MainWindow::initializeWidgets()
{
	// initialize pointers to NULLs
	treeViewCoursesAndLessons_ = 0;
	treeViewLessons_ = 0;
	menuItemQuit_ = 0;
	toolButtonConnect_ = 0;
	toolButtonDisconnect_ = 0;
	loginDialog_ = 0;
	user_ = 0;
	courses_ = 0;
	lessons_ = 0;
	frameCourse_ = 0;
	frameLesson_ = 0;
	labelLessonName_ = 0;
	labelLessonDescription_ = 0;
	labelLessonDate_ = 0;
	labelCourseStartDate_ = 0;
	labelCourseEndDate_ = 0;
	labelCourseName_ = 0;
	labelCourseDescription_ = 0;
	labelScreencastingActive_ = 0;
	buttonInstructorStartPauseLesson_ = 0;
	buttonInstructorFinishLesson_ = 0;
	buttonInstructorLessonChat_ = 0;
	buttonStudentJoinLesson_ = 0;
	checkButtonScreencastPersist_ = 0;

	// get widgets
	builder_->get_widget("main_imagemenuitem_quit", menuItemQuit_);
	builder_->get_widget("main_imagemenuitem_connect", menuItemConnect_);
	builder_->get_widget("main_imagemenuitem_disconnect", menuItemDisconnect_);
	builder_->get_widget("main_toolbutton_connect", toolButtonConnect_);
	builder_->get_widget("main_toolbutton_disconnect", toolButtonDisconnect_);
	builder_->get_widget("main_treeview", treeViewCoursesAndLessons_);
	builder_->get_widget("main_frame_course", frameCourse_);
	builder_->get_widget("main_frame_lesson", frameLesson_);
	builder_->get_widget("main_frame_lesson_label_name_value", labelLessonName_);
	builder_->get_widget("main_frame_lesson_label_description_value", labelLessonDescription_);
	builder_->get_widget("main_frame_course_label_name_value", labelCourseName_);
	builder_->get_widget("main_frame_course_label_description_value", labelCourseDescription_);
	builder_->get_widget("main_frame_lesson_label_date_value", labelLessonDate_);
	builder_->get_widget("main_frame_course_label_start_date_value", labelCourseStartDate_);
	builder_->get_widget("main_frame_course_label_end_date_value", labelCourseEndDate_);
	builder_->get_widget("main_frame_lesson_label_screencasting_active", labelScreencastingActive_);
	builder_->get_widget("main_frame_lesson_button_start_pause", buttonInstructorStartPauseLesson_);
	builder_->get_widget("main_frame_lesson_button_finish", buttonInstructorFinishLesson_);
	builder_->get_widget("main_frame_lesson_button_chat", buttonInstructorLessonChat_);
	builder_->get_widget("main_frame_lesson_button_join", buttonStudentJoinLesson_);
	builder_->get_widget("main_frame_course_treeview_lessons", treeViewLessons_);
	builder_->get_widget("main_frame_lesson_checkbutton_persist", checkButtonScreencastPersist_);

	// check if successfull
	assert(menuItemQuit_ != 0 && "menuItemQuit_ pointer is NULL!");
	assert(menuItemConnect_ != 0 && "menuItemConnect_ pointer is NULL!");
	assert(menuItemDisconnect_ != 0 && "menuItemDisconnect_ pointer is NULL!");
	assert(toolButtonConnect_ != 0 && "toolButtonConnect_ pointer is NULL!");
	assert(toolButtonDisconnect_ != 0 && "toolButtonDisconnect_ pointer is NULL!");
	assert(treeViewCoursesAndLessons_ != 0 && "treeViewCoursesAndLessons_ pointer is NULL!");
	assert(frameCourse_ != 0 && "frameCourse_ pointer is NULL!");
	assert(frameLesson_ != 0 && "frameLesson_ pointer is NULL!");
	assert(labelLessonName_ != 0 && "labelLessonName_ pointer is NULL!");
	assert(labelLessonDescription_ != 0 && "labelLessonDescription_ pointer is NULL!");
	assert(labelCourseName_ != 0 && "labelCourseName_ pointer is NULL!");
	assert(labelCourseDescription_ != 0 && "labelCourseDescription_ pointer is NULL!");
	assert(labelLessonDate_ != 0 && "labelLessonDate_ pointer is NULL!");
	assert(labelCourseStartDate_ != 0 && "labelCourseStartDate_ pointer is NULL!");
	assert(labelCourseEndDate_ != 0 && "labelCourseEndDate_ pointer is NULL!");
	assert(labelScreencastingActive_ != 0 && "labelScreencastingActive_ pointer is NULL!");
	assert(buttonInstructorStartPauseLesson_ != 0 && "toggleButtonInstructorStartPauseLesson_ pointer is NULL!");
	assert(buttonInstructorFinishLesson_ != 0 && "buttonInstructorFinishLesson_ pointer is NULL!");
	assert(buttonInstructorLessonChat_ != 0 && "buttonInstructorFinishLesson_ pointer is NULL!");
	assert(buttonStudentJoinLesson_ != 0 && "buttonStudentJoinLesson_ pointer is NULL!");
	assert(treeViewLessons_ != 0 && "treeViewLessons_ pointer is NULL!");
	assert(checkButtonScreencastPersist_ != 0 && "checkButtonScreencastPersist_ pointer is NULL!");

	// create the tree model
	treeModelCoursesAndLessons_ = Gtk::TreeStore::create(treeModelCoursesAndClasesColumns_);
	treeModelCoursesAndLessonsFilter_ = Gtk::TreeModelFilter::create(treeModelCoursesAndLessons_);
	treeViewCoursesAndLessons_->set_model(treeModelCoursesAndLessons_);
	treeViewLessons_->set_model(treeModelCoursesAndLessonsFilter_);
	treeViewCoursesAndLessons_->set_headers_visible(false);
	treeViewLessons_->set_headers_visible(false);
	treeViewLessons_->append_column("", treeModelCoursesAndClasesColumns_.columnName_);
}

/**
 *
 */
void MainWindow::makeConnections()
{
	// quit
	menuItemQuit_->signal_activate().connect(sigc::mem_fun(*this, &MainWindow::onQuitRequested));

	// connect
	menuItemConnect_->signal_activate().connect(sigc::mem_fun(*this, &MainWindow::onConnectRequested));
	toolButtonConnect_->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::onConnectRequested));

	// disconnect
	menuItemDisconnect_->signal_activate().connect(sigc::mem_fun(*this, &MainWindow::onDisconnectRequested));
	toolButtonDisconnect_->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::onDisconnectRequested));

	// tree view row activated
	treeViewCoursesAndLessons_->signal_row_activated().connect(sigc::mem_fun(*this, &MainWindow::onItemInfoRequested));
	treeViewLessons_->signal_row_activated().connect(sigc::mem_fun(*this, &MainWindow::onItemInfoRequested));

	// handle screencasting capture
	buttonInstructorStartPauseLesson_->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::onInstructorStartPauseButtonClicked));
	buttonStudentJoinLesson_->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::onStudentJoinLessonCicked));
	buttonInstructorFinishLesson_->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::onInstructorFinalizeScreencastButtonClicked));

	// filtering in tree views
	treeModelCoursesAndLessonsFilter_->set_visible_func(sigc::mem_fun(*this, &MainWindow::onFilterRowVisible));

	// handle closure of screencasting window
	screencastingWindow_->signal_hide().connect(sigc::mem_fun(*this, &MainWindow::onScreencastingWindowClosure));
	screencastingWindow_->signal_show().connect(sigc::mem_fun(*this, &MainWindow::onScreencastingWindowOpen));

	// reopen screencast window
	buttonInstructorLessonChat_->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::onInstructorLessonChatButtonClicked));
}

/**
 *
 */
void MainWindow::onQuitRequested()
{
	hide();
}

void MainWindow::lessonNotAvailable(Lesson* lesson){
  //labelScreencastingActive_->set_text("LESSON NOT AVAILABLE");
  //if(user_->isInstructor())
  //buttonInstructorStartPauseLesson_->set_visible(true);
  screencastingWindow_->hide();
  showErrorMessageDialog("Lesson not available", "Lesson you have requested is not available at the moment...");
}
/**
 *
 */
void MainWindow::onConnectRequested()
{
	loginDialog_->show();
}

/**
 *
 */
void MainWindow::onDisconnectRequested()
{
	freeResources();

	set_title("Screencasting Teaching System");

	toolButtonDisconnect_->set_sensitive(false);
	menuItemDisconnect_->set_sensitive(false);
	toolButtonConnect_->set_sensitive(true);
	menuItemConnect_->set_sensitive(true);
	treeModelCoursesAndLessons_->clear();
	treeViewCoursesAndLessons_->remove_all_columns();
	treeViewCoursesAndLessons_->set_sensitive(false);
	frameCourse_->set_visible(false);
	frameLesson_->set_visible(false);
	ServerService::getInstance().logout();
}

/**
 *
 */
void MainWindow::onLoginSuccessfull(User *user)
{
	this->user_ = user;
	assert(user_ != 0 && "user_ pointer is NULL!");

	set_title("Screencasting Teaching System (Connected - user: " + user->getName() + ", role: " + (user->getInstructor() ? "Instructor" : "Student") + ")");

	toolButtonDisconnect_->set_sensitive(true);
	menuItemDisconnect_->set_sensitive(true);
	toolButtonConnect_->set_sensitive(false);
	menuItemConnect_->set_sensitive(false);
	treeViewCoursesAndLessons_->set_sensitive(true);


	screencastingWindow_->setUserName(user->getName());
}

/**
 *
 */
void MainWindow::freeResources()
{
	if (user_) {
		delete user_;
		user_ = 0;
	}
	if (lessons_) {
		while (!lessons_->empty()) {
			delete lessons_->back();
			lessons_->pop_back();
		}
		delete lessons_;
		lessons_ = 0;
	}
	if (courses_) {
		while (!courses_->empty()) {
			delete courses_->back();
			courses_->pop_back();
		}
		delete courses_;
		courses_ = 0;
	}
}

/**
 *
 */
void MainWindow::onShowsListRequested(std::list<Course*> *courses, std::list<Lesson*> *lessons)
{
	this->courses_ = courses;
	this->lessons_ = lessons;
	assert(courses_ != 0 && "courses_ pointer is NULL!");
	assert(lessons_ != 0 && "lessons_ pointer is NULL!");

	// fill the tree view's model with available courses and its
	// corresponded lessons
	treeViewCoursesAndLessons_->append_column("", treeModelCoursesAndClasesColumns_.columnName_);
	for (std::list<Course*>::const_iterator i = courses_->begin(); i != courses_->end(); ++i) {
		Gtk::TreeModel::Row row = *(treeModelCoursesAndLessons_->append());
		row[treeModelCoursesAndClasesColumns_.columnName_] = (*i)->getName();
		row[treeModelCoursesAndClasesColumns_.columnCoursePointer_] = *i;
		row[treeModelCoursesAndClasesColumns_.columnLessonPointer_] = 0;

		for (std::list<Lesson*>::const_iterator j = (*i)->getLessons().begin(); j != (*i)->getLessons().end(); ++j) {
			Gtk::TreeModel::Row childrow = *(treeModelCoursesAndLessons_->append(row.children()));
			childrow[treeModelCoursesAndClasesColumns_.columnName_] = (*j)->getName();
			childrow[treeModelCoursesAndClasesColumns_.columnLessonPointer_] = *j;
			childrow[treeModelCoursesAndClasesColumns_.columnCoursePointer_] = *i;
		}
	}

	// with lessons without a course
	for (std::list<Lesson*>::const_iterator i = lessons_->begin(); i != lessons_->end(); ++i) {
		Gtk::TreeModel::Row row = *(treeModelCoursesAndLessons_->append());
		row[treeModelCoursesAndClasesColumns_.columnName_] = (*i)->getName();
		row[treeModelCoursesAndClasesColumns_.columnLessonPointer_] = *i;
		row[treeModelCoursesAndClasesColumns_.columnCoursePointer_] = 0;
	}

	treeViewCoursesAndLessons_->expand_all();
}

/**
 *
 */
void MainWindow::onItemInfoRequested(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column)
{
  Gtk::TreeModel::iterator iter = treeModelCoursesAndLessons_->get_iter(path);

  if(iter) {
    Gtk::TreeModel::Row row = *iter;
    selectedCourse_ = row[treeModelCoursesAndClasesColumns_.columnCoursePointer_];
    selectedLesson_ = row[treeModelCoursesAndClasesColumns_.columnLessonPointer_];

    labelScreencastingActive_->set_text("");
    if (selectedCourse_ && !selectedLesson_)
    	setCourseInfo();
    else
    	setLessonInfo();
  }
}

/**
 *
 */
void MainWindow::setLessonInfo()
{
	// common attributes
	labelLessonName_->set_text(selectedLesson_->getName().c_str());
	labelLessonDescription_->set_text(selectedLesson_->getDescription().c_str());

	time_t programmedDate = selectedLesson_->getProgrammedDate();
	char programmedDateString[80];
	struct tm *ts = localtime(&programmedDate);
	strftime(programmedDateString, sizeof(programmedDateString), "%a %Y-%m-%d %H:%M:%S", ts);

	labelScreencastingActive_->set_visible(screencastingActive_);
	checkButtonScreencastPersist_->set_sensitive(!screencastingActive_);
	treeViewCoursesAndLessons_->set_sensitive(!screencastingActive_);

	// instructor's attributes
	if (user_->isInstructor()) {
		// The instructor may also join older screencast
		buttonStudentJoinLesson_->set_visible(true);
		buttonInstructorStartPauseLesson_->set_visible(true);
		buttonInstructorFinishLesson_->set_sensitive(screencastingActive_);
		buttonInstructorLessonChat_->set_sensitive(screencastingActive_ && !screencastingWindow_->is_visible());
	// student's attributes
	} else {
		buttonStudentJoinLesson_->set_sensitive(!screencastingActive_);
		checkButtonScreencastPersist_->set_visible(false);
		buttonInstructorStartPauseLesson_->set_visible(false);
		buttonInstructorFinishLesson_->set_visible(false);
		buttonInstructorLessonChat_->set_visible(false);
	}

	// frames
	frameCourse_->set_visible(false);
	frameLesson_->set_visible(true);
}

/**
 *
 */
void MainWindow::setCourseInfo()
{
	labelCourseName_->set_text(selectedCourse_->getName().c_str());
	labelCourseDescription_->set_text(selectedCourse_->getDescription().c_str());

	time_t startDate = selectedCourse_->getStartDate();
	char startDateString[80];
	struct tm *ts1 = localtime(&startDate);
	strftime(startDateString, sizeof(startDateString), "%a %Y-%m-%d %H:%M:%S", ts1);
	labelCourseStartDate_->set_text(startDateString);

	time_t endDate = selectedCourse_->getEndDate();
	char endDateString[80];
	struct tm *ts2 = localtime(&endDate);
	strftime(endDateString, sizeof(endDateString), "%a %Y-%m-%d %H:%M:%S", ts2);
	labelCourseEndDate_->set_text(endDateString);

	treeModelCoursesAndLessonsFilter_->refilter();
	treeViewLessons_->expand_all();

	frameLesson_->set_visible(false);
	frameCourse_->set_visible(true);
}

/**
 *
 */
bool MainWindow::onFilterRowVisible(const Gtk::TreeModel::const_iterator& iter) {
	return (iter->get_value(treeModelCoursesAndClasesColumns_.columnCoursePointer_) == selectedCourse_);
}

/**
 *
 */
void MainWindow::onStudentJoinLessonCicked()
{
  buttonInstructorStartPauseLesson_->set_visible(false);
  screencastingActive_ = true;

  LOG4CXX_INFO(logger_, "Starting reception");
  screencastingWindow_->setCapturedLesson(selectedLesson_);
  screencastingWindow_->show();
  screencastingWindow_->play();
  capturedLesson_ = selectedLesson_;
  ServerService::getInstance().playScreencast(selectedLesson_);
  setLessonInfo();
}

/**
 *
 */
void MainWindow::onInstructorStartPauseButtonClicked()
{
	// change state
	screencastingActive_ = !screencastingActive_;

	if (screencastingActive_) {
		buttonInstructorStartPauseLesson_->set_label("Pause Screencast");
	} else {
		buttonInstructorStartPauseLesson_->set_label("Start Screencast");
	}

	if (screencastingActive_) {
		LOG4CXX_INFO(logger_, "Starting screencast of current workspace");
		screencastingWindow_->setCapturedLesson(selectedLesson_);
		screencastingWindow_->show();
		capturedLesson_ = selectedLesson_;
		if(checkButtonScreencastPersist_->get_active()) {
		    ServerService::getInstance().startAndSaveScreencast(selectedLesson_);
		} else {
			ServerService::getInstance().startScreencast(selectedLesson_);
		}
		captureWorkspace_->startScreencast(selectedLesson_);
	} else {
		LOG4CXX_DEBUG(logger_, "Terminating screencast of current workspace");
		screencastingWindow_->hide();
		captureWorkspace_->stopScreencast();
	}

	setLessonInfo();
}

/**
 *
 */
void MainWindow::onInstructorLessonChatButtonClicked() {
	screencastingWindow_->show();
	buttonInstructorLessonChat_->set_sensitive(false);
}

/**
 *
 */
void MainWindow::onScreencastingWindowClosure() {
	screencastingActive_ = false;
	ServerService::getInstance().stopListeningScreencast(capturedLesson_);
	std::cout << "stopListening called" << std::endl;
	screencastingWindow_->stop();
	std::cout << "stop player called" << std::endl;
	setLessonInfo();
	std::cout << "ASDSADSAd" << std::endl;
}


/**
 *
 */
void MainWindow::onInstructorFinalizeScreencastButtonClicked() {
	if (user_->isInstructor()) {
		buttonInstructorStartPauseLesson_->set_label("Start Screencast");
		screencastingActive_ = false;
		captureWorkspace_->stopScreencast();
		ServerService::getInstance().stopScreencast(selectedLesson_);
	}

	setLessonInfo();
}

/**
 *
 */
void MainWindow::showErrorMessageDialog(const Glib::ustring &primary, const Glib::ustring &secondary) {
	Gtk::MessageDialog messageDialog(*this, primary, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, false);
	messageDialog.set_secondary_text(secondary);
	messageDialog.run();
}

/**
 *
 */
void MainWindow::onScreencastingWindowOpen() {
	screencastingActive_ = true;
}
